package com.ccteam.shared.core

import android.content.Context
import androidx.datastore.core.CorruptionException
import androidx.datastore.core.Serializer
import androidx.datastore.dataStore
import com.ccteam.shared.datastore.protobuf.UserInfoProtos
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import java.io.InputStream
import java.io.OutputStream
import javax.inject.Inject
import javax.inject.Singleton

/**
 * @author Xiaoc
 * @since 2021/3/17
 *
 * 用户暂存信息，用于快速得到用户登录等相关信息
 * 该类由Hilt管理，为单例类
 */
@Singleton
class UserCore @Inject constructor(
    @ApplicationContext private val context: Context
) {
    // 协程组件
    private val serviceJob = SupervisorJob()
    private val serviceScope = CoroutineScope(Dispatchers.Main + serviceJob)

    /**
     * 是否登录成功，false 处于未登录 true 为已登录
     */
    private val _isLogin = MutableStateFlow(false)
    val isLogin get() = _isLogin.asStateFlow()

    private val _userInfo = MutableStateFlow<UserInfoProtos.UserInfo>(
        UserInfoProtos.UserInfo.getDefaultInstance()
    )
    val userInfo get() = _userInfo.asStateFlow()

    init {
        observeLoginState()
    }

    /**
     * 读取登录状态
     */
    fun readLoginState(): Flow<UserInfoProtos.UserInfo>{
        return context.userInfoDataStore.data
    }

    /**
     * 刷新当前登录状态
     * 当进行了登录操作，应该调用此方法进行更新，因为登录状态已经发生变化
     */
    private fun observeLoginState(){
        serviceScope.launch {
            context.userInfoDataStore.data.collectLatest {
                _userInfo.value = it
                _isLogin.value = !it.userId.isNullOrEmpty() && !it.userToken.isNullOrEmpty()
            }
        }
    }

    /**
     * 登录账号，会更新当前在线状态
     * @param id 用户ID
     * @param userToken 用户Token
     */
    fun login(id: String,userToken: String){
        serviceScope.launch {
            context.userInfoDataStore.updateData {
                it.toBuilder().setUserId(id)
                    .setUserToken(userToken).build()
            }
        }
    }

    /**
     * 登出账号
     */
    fun logout(){
        serviceScope.launch {
            context.userInfoDataStore.updateData { userInfo ->
                userInfo.defaultInstanceForType
            }
        }
    }
}

private const val userInfoDataStoreFileName = "userInfo.pb"

/**
 * 当前用户信息暂存的DataStore
 */
private val Context.userInfoDataStore by dataStore(
    fileName = userInfoDataStoreFileName,
    serializer = UserInfoSerializer
)

/**
 * [UserInfoProtos.UserInfo] 的序列化器
 * 为什么要写这个是因为Java自带序列化用了耗费资源性能的反射
 * 而Android利用该序列化器将其显式调用防止耗费多余性能
 */
@Suppress("BlockingMethodInNonBlockingContext")
private object UserInfoSerializer: Serializer<UserInfoProtos.UserInfo> {
    override val defaultValue: UserInfoProtos.UserInfo
        get() = UserInfoProtos.UserInfo.getDefaultInstance()

    override suspend fun readFrom(input: InputStream): UserInfoProtos.UserInfo {
        return withContext(Dispatchers.IO){
            try {
                // 是编译器自动生成的，用于读取并解析 input 的消息
                return@withContext UserInfoProtos.UserInfo.parseFrom(input)
            } catch (e: Exception){
                throw CorruptionException("无法读取Proto内容 ${e.message}")
            }
        }

    }

    override suspend fun writeTo(t: UserInfoProtos.UserInfo, output: OutputStream) {
        // t.writeTo(output) 是编译器自动生成的，用于写入序列化消息
        withContext(Dispatchers.IO){
            t.writeTo(output)
        }
    }

}

